Analysis of the dataset of discovered exoplanets (2021) - Group D (Teggi, Verdolin)¶
link to the dataset: https://www.kaggle.com/datasets/shivamb/all-exoplanets-dataset?resource=download
Data import, filtering and preparation for analysis¶
In [1]:
import pandas as pd
import numpy as np
import plotly.express as px
import plotly.subplots as sp
import plotly.graph_objects as go
# Load the dataset
dataset_path = "data/exoplanets/all_exoplanets_2021.csv"
exoplanet_data = pd.read_csv(dataset_path)
# Filter the columns from the total 23
selected_columns = [
"Planet Name", # string (name of the planet)
"Planet Host", # string (name of the star system)
"Num Stars", # int (number of stars in that star system)
"Num Planets", # int (number of planets discovered in that star system)
"Discovery Method", # string (method used)
"Discovery Year", # int (year of discovery)
"Discovery Facility", # string (observatory)
"Orbital Period Days", # double (orbital period of the planet around the star [earth days])
"Orbit Semi-Major Axis", # double (greater distance from the star [AU (149 597 870 700 m)])
"Mass", # double (mass of the planet [earth mass])
"Stellar Effective Temperature", # double (average surface temperature of the star [Kelvin])
"Stellar Radius", # double (radius of the star [solar radius])
"Stellar Mass", # double (mass of the star [solar masses])
"Stellar Surface Gravity", # double (average surface gravity [log g])
"Distance" # double (distance from our system [Parsec])
]
filtered_data = exoplanet_data[selected_columns]
filtered_data.head(5)
Out[1]:
| Planet Name | Planet Host | Num Stars | Num Planets | Discovery Method | Discovery Year | Discovery Facility | Orbital Period Days | Orbit Semi-Major Axis | Mass | Stellar Effective Temperature | Stellar Radius | Stellar Mass | Stellar Surface Gravity | Distance | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 11 Com b | 11 Com | 2 | 1 | Radial Velocity | 2007 | Xinglong Station | 326.03000 | 1.29 | 6165.6000 | 4742.0 | 19.00 | 2.70 | 2.31 | 93.1846 |
| 1 | 11 UMi b | 11 UMi | 1 | 1 | Radial Velocity | 2009 | Thueringer Landessternwarte Tautenburg | 516.21997 | 1.53 | 4684.8142 | 4213.0 | 29.79 | 2.78 | 1.93 | 125.3210 |
| 2 | 14 And b | 14 And | 1 | 1 | Radial Velocity | 2008 | Okayama Astrophysical Observatory | 185.84000 | 0.83 | 1525.5000 | 4813.0 | 11.00 | 2.20 | 2.63 | 75.4392 |
| 3 | 14 Her b | 14 Her | 1 | 2 | Radial Velocity | 2002 | W. M. Keck Observatory | 1773.40002 | 2.93 | 1481.0878 | 5338.0 | 0.93 | 0.90 | 4.45 | 17.9323 |
| 4 | 16 Cyg B b | 16 Cyg B | 3 | 1 | Radial Velocity | 1996 | Multiple Observatories | 798.50000 | 1.66 | 565.7374 | 5750.0 | 1.13 | 1.08 | 4.36 | 21.1397 |
In [2]:
filtered_data.shape
Out[2]:
(4575, 15)
Basic analysis¶
Number of Discovered Planets per Year¶
In [3]:
planets_per_year = filtered_data['Discovery Year'].value_counts().sort_index()
# Creare un istogramma ridimensionato con proporzioni mantenute
discovery_year_histogram = px.bar(
planets_per_year,
x=planets_per_year.index,
y=planets_per_year.values,
labels={'x': 'Discovery Year', 'y': 'Number of Planets Discovered'},
title='Number of Exoplanets Discovered Per Year',
text_auto=True # Mostra i valori sopra le barre
)
# Modificare l'asse X per aggiungere tick con linee visibili
discovery_year_histogram.update_layout(
yaxis=dict(
type="log", # Scala logaritmica
title="Number of Planets Discovered (Log Scale)"
),
xaxis=dict(
title="Discovery Year",
tickmode="linear", # Tick lineari
dtick=1, # Tick ogni anno
ticklen=5, # Lunghezza delle righe dei tick
tickwidth=1, # Spessore delle righe dei tick
tickvals=planets_per_year.index, # Mostra righe dei tick per ogni anno
showline=True, # Mostra la linea dell'asse
showgrid=False # Disabilita le linee della griglia
),
title=dict(
x=0.5 # Centrare il titolo
),
width=1280, # Larghezza della figura (ridotta per powerpioint)
height=720
)
# Visualizzare il grafico
discovery_year_histogram.show()
Number of Stars and Planets per System¶
In [4]:
# Contare la frequenza di stelle per sistema
stars_per_system = filtered_data['Num Stars'].value_counts()
# Contare la frequenza di pianeti per sistema
planets_per_system = filtered_data['Num Planets'].value_counts()
# Creare la sottotrama
fig = sp.make_subplots(
rows=1, cols=2,
subplot_titles=['Number of Stars per System', 'Number of Planets per System']
)
# Aggiungere il grafico istogramma per il numero di stelle per sistema
fig.add_trace(
go.Bar(
x=stars_per_system.index,
y=stars_per_system.values,
name="Stars per System"
),
row=1, col=1
)
# Aggiungere il grafico istogramma per il numero di pianeti per sistema
fig.add_trace(
go.Bar(
x=planets_per_system.index,
y=planets_per_system.values,
name="Planets per System"
),
row=1, col=2
)
# Modificare il layout
fig.update_layout(
title_text="Distribution of the Number of Stars and Planets per System",
showlegend=False, # Rimuove la legenda
width=1280, # Larghezza per slide PowerPoint
height=720 # Altezza per slide PowerPoint
)
# Impostare l'asse Y in scala logaritmica per entrambi i grafici
fig.update_yaxes(type="log", row=1, col=1) # Grafico di sinistra
fig.update_yaxes(type="log", row=1, col=2) # Grafico di destra
# Configurare i tick sull'asse X per mostrare solo valori interi
fig.update_xaxes(
tickmode="array",
tickvals=stars_per_system.index,
ticktext=[str(int(x)) for x in stars_per_system.index], # Mostra solo valori interi come etichette
row=1, col=1
)
fig.update_xaxes(
tickmode="array",
tickvals=planets_per_system.index,
ticktext=[str(int(x)) for x in planets_per_system.index], # Mostra solo valori interi come etichette
row=1, col=2
)
# Visualizzare il grafico
fig.show()
Top 5 most and least massive exoplanets¶
In [5]:
# Trovare i 5 pianeti più massicci e i 5 meno massicci
most_massive_planets = filtered_data.nlargest(5, "Mass")[["Planet Name", "Mass"]]
least_massive_planets = filtered_data.nsmallest(5, "Mass")[["Planet Name", "Mass"]]
# Creare la sottotrama
fig = sp.make_subplots(
rows=1, cols=2,
subplot_titles=["5 Most Massive Planets", "5 Least Massive Planets"],
horizontal_spacing=0.2
)
# Aggiungere il grafico per i 5 pianeti più massicci (asse invertito)
fig.add_trace(
go.Bar(
y=most_massive_planets["Planet Name"],
x=most_massive_planets["Mass"],
name="Most Massive Planets",
orientation="h" # Orientamento orizzontale
),
row=1, col=1
)
# Aggiungere il grafico per i 5 pianeti meno massicci (asse invertito)
fig.add_trace(
go.Bar(
y=least_massive_planets["Planet Name"],
x=least_massive_planets["Mass"],
name="Least Massive Planets",
orientation="h" # Orientamento orizzontale
),
row=1, col=2
)
# Modificare il layout per PowerPoint
fig.update_layout(
title_text="Most and Least Massive Planets",
showlegend=False,
yaxis_title="Planet Name (Most Massive)",
xaxis_title="Mass [Earth Masses]",
yaxis2_title="Planet Name (Least Massive)",
xaxis2_title="Mass [Earth Masses]",
width=1280, # Larghezza per PowerPoint
height=720 # Altezza per PowerPoint
)
# Allineare meglio le etichette
fig.update_yaxes(autorange="reversed", row=1, col=1) # Ordinamento inverso per migliorare leggibilità
fig.update_yaxes(autorange="reversed", row=1, col=2)
# Visualizzare il grafico
fig.show()
Top 5 most and least massive stars¶
In [6]:
# Filtrare i dati per escludere valori nulli
filtered_data_non_null = filtered_data.dropna(subset=["Stellar Radius"])
# Trovare i valori di raggio più piccolo e più grande
min_radius = filtered_data_non_null["Stellar Radius"].min()
max_radius = filtered_data_non_null["Stellar Radius"].max()
sun_radius = 1 # Raggio del Sole (unità di misura)
# Creare un DataFrame con i dati per il confronto
radius_comparison = pd.DataFrame({
"Star": ["Smallest Star", "Sun (Reference)", "Largest Star"],
"Radius [Solar Radii]": [min_radius, sun_radius, max_radius],
"Radius for Plot": [min_radius * 10, sun_radius * 10, max_radius * 10] # Ingrandire per visibilità
})
# Creare il grafico scatter con cerchi proporzionali al raggio
fig = px.scatter(
radius_comparison,
x=["Smallest Star", "Sun (Reference)", "Largest Star"], # Posizioni fisse sull'asse X
y=[0, 0, 0], # Tutti i punti sono sulla stessa linea (visivamente centrati)
size="Radius for Plot", # Proporzionalità del cerchio
size_max=250, # Limite massimo per i cerchi
color_discrete_sequence=["orange"], # Colore arancione per i cerchi
labels={"x": "Star dimension", "size": "Stellar Radius (Solar Radii)"},
title="Comparison of Stellar sizes: Smallest Star, Sun, Largest Star",
template="plotly_white"
)
# Personalizzare il layout
fig.update_layout(
width=1280, # Dimensioni per slide PowerPoint
height=720,
showlegend=False, # Rimuovere legenda
yaxis=dict(visible=False), # Nascondere l'asse Y
xaxis_title="Stars",
xaxis=dict(showgrid=False) # Nascondere la griglia
)
# Mostrare il grafico
fig.show()
Discovery methods analysis¶
Discovery Methods over time (min 10 entries)¶
In [7]:
# Filtrare i metodi con almeno 5 scoperte totali
method_totals = filtered_data["Discovery Method"].value_counts()
valid_discovery_methods = method_totals[method_totals >= 10].index
filtered_discovery_data = filtered_data[filtered_data["Discovery Method"].isin(valid_discovery_methods)]
# Raggruppare per anno e metodo di scoperta e contare il numero di pianeti scoperti
discovery_trend_filtered = (
filtered_discovery_data.groupby(["Discovery Year", "Discovery Method"])["Planet Name"]
.count()
.reset_index(name="Count")
)
# Creare il grafico a linee con dimensioni per slide PowerPoint
fig = px.line(
discovery_trend_filtered,
x="Discovery Year",
y="Count",
color="Discovery Method",
labels={
"Discovery Year": "Year",
"Count": "Number of Planets Discovered"
},
title="Trend of Discovery Methods Over Time",
template="plotly_white"
)
# Impostare l'asse Y in scala logaritmica
fig.update_layout(
yaxis=dict(
type="log", # Scala logaritmica
title="Number of Planets Discovered (Log Scale)"
),
title=dict(
x=0.5 # Centrare il titolo
),
width=1280, # Larghezza
height=720 # Altezza
)
# Visualizzare il grafico
fig.show()
Discovery Methods and Distances¶
In [8]:
from plotly.subplots import make_subplots
# Filtrare i metodi di scoperta con almeno 3 entries
method_counts = filtered_data["Discovery Method"].value_counts()
filtered_methods = method_counts[method_counts >= 3].index
# Filtrare i dati originali per i metodi selezionati
filtered_discovery_data = filtered_data[filtered_data["Discovery Method"].isin(filtered_methods)]
# Conteggio dei metodi dopo il filtraggio e ordinamento in ordine crescente
method_counts_filtered = filtered_discovery_data["Discovery Method"].value_counts().sort_values(ascending=True)
# Creazione del grafico subplot
fig = make_subplots(
rows=1,
cols=2,
subplot_titles=("Number of Planets Discovered by Method", "Distance Distribution by Method"),
horizontal_spacing=0.15,
shared_yaxes=True # Condivide l'asse Y
)
# Primo subplot: Istogramma del numero di pianeti scoperti per metodo
fig.add_trace(
go.Bar(
x=method_counts_filtered.values,
y=method_counts_filtered.index,
orientation='h', # Orientazione orizzontale
text=method_counts_filtered.values, # Aggiungere il numero effettivo come testo
textposition='auto', # Posizionare il testo automaticamente
name="Number of Planets"
),
row=1,
col=1
)
# Secondo subplot: Boxplot delle distanze per metodo
fig.add_trace(
go.Box(
x=filtered_discovery_data["Distance"],
y=filtered_discovery_data["Discovery Method"],
name="Distance Distribution",
orientation='h', # Orientazione orizzontale
),
row=1,
col=2
)
# Impostare l'asse X del primo subplot in scala logaritmica
fig.update_xaxes(
type="log", # Scala logaritmica
title_text="Number of Planets (Log Scale)",
row=1,
col=1
)
# Configurazione del layout generale per PowerPoint
fig.update_layout(
title_text="Discovery Methods Analysis: Number of Planets and Distance Distribution",
xaxis2_title="Distance [pc]", # Asse X del secondo subplot
yaxis_title="Discovery Method",
showlegend=False,
height=720, # Altezza per slide PowerPoint
width=1280, # Larghezza per slide PowerPoint
template="plotly_white"
)
# Mostrare il grafico
fig.show()
Correlation Between Planet Mass and Orbital Distance Across Discovery Methods¶
In [9]:
# Filtrare i metodi di scoperta con almeno 5 entries
method_counts = filtered_data["Discovery Method"].value_counts()
filtered_methods = method_counts[method_counts >= 10].index
data_for_scatter = filtered_data[filtered_data["Discovery Method"].isin(filtered_methods)]
In [10]:
# Creare uno scatter plot formattato per PowerPoint
fig = px.scatter(
data_for_scatter,
x="Mass",
y="Orbit Semi-Major Axis",
color="Discovery Method",
labels={
"Mass": "Planet Mass [Earth Masses]",
"Orbit Semi-Major Axis": "Planet Orbit Semi-Major Axis [AU]"
},
title="Planet Mass vs Planet Orbit Semi-Major Axis",
template="plotly_white",
log_x=True, # Scala logaritmica sull'asse X
log_y=True # Scala logaritmica sull'asse Y
)
# Formattare il grafico per PowerPoint
fig.update_layout(
width=1280, # Larghezza per PowerPoint
height=720 # Altezza per PowerPoint
)
# Mostrare il grafico
fig.show()
Other analysis¶
Top 9 Discovery Facilities by Number of Planets Discovered¶
In [11]:
# Conteggiare il numero di pianeti scoperti da ciascuna struttura
facility_counts = filtered_data['Discovery Facility'].value_counts()
# Separare i primi 10 osservatori
top_10_facilities = facility_counts.head(10)
# Calcolare il totale di "Others" includendo anche "Multiple Observatories"
others_count = facility_counts[10:].sum()
if "Multiple Observatories" in top_10_facilities.index:
others_count += top_10_facilities["Multiple Observatories"]
top_10_facilities = top_10_facilities.drop("Multiple Observatories")
# Aggiungere "Others" ai dati
top_10_facilities_with_others = pd.concat([top_10_facilities, pd.Series({"Others": others_count})])
# Ordinare i valori (escludendo temporaneamente "Others")
top_10_facilities_sorted = top_10_facilities_with_others.drop("Others").sort_values(ascending=True)
# Aggiungere "Others" all'inizio
top_10_facilities_sorted = pd.concat([pd.Series({"Others": others_count}), top_10_facilities_sorted])
# Creare l'istogramma
facility_histogram = px.bar(
top_10_facilities_sorted,
y=top_10_facilities_sorted.index,
x=top_10_facilities_sorted.values,
labels={'x': 'Number of Planets Discovered', 'y': 'Discovery Facility'},
title='Top Discovery Facilities by Number of Planets Discovered',
text_auto=True # Mostra i valori sopra le barre
)
# Aggiungere opzioni per migliorare la leggibilità
facility_histogram.update_layout(
yaxis_title="Discovery Facility",
xaxis_title="Number of Planets Discovered",
template="plotly_white", # Tema chiaro
width=1280, # Larghezza per slide PowerPoint
height=720 # Altezza per slide PowerPoint
)
# Lista di osservatori non terrestri
custom_space_facilities = ["Kepler", "K2", "Transiting Exoplanet Survey Satellite (TESS)"]
# Colori specifici per le barre
facility_histogram.update_traces(marker_color=[
'gray' if i == "Others" else
'#636EFA' if i in custom_space_facilities else
'red' for i in top_10_facilities_sorted.index
])
# Aggiungere la legenda personalizzata
facility_histogram.add_trace(
go.Scatter(
x=[None], y=[None],
mode="markers",
marker=dict(size=10, color='#636EFA'),
name="Space Observatories"
)
)
facility_histogram.add_trace(
go.Scatter(
x=[None], y=[None],
mode="markers",
marker=dict(size=10, color='red'),
name="Ground-based Observatories"
)
)
# Visualizzare il grafico
facility_histogram.show()
Orbital Period vs Semi-Major Axis Colored by Planet Mass¶
In [12]:
# Filtrare i dati per escludere valori nulli nella colonna della massa
filtered_data_non_null = filtered_data.dropna(subset=["Orbit Semi-Major Axis", "Orbital Period Days", "Mass"])
# Creare una colonna personalizzata per limitare la massa a 2000 per visualizzare meglio il gradiente
filtered_data_non_null["Mass (Capped)"] = filtered_data_non_null["Mass"].apply(lambda x: min(x, 2000))
# Creare uno scatter plot per confrontare distanza e periodo orbitale con gradiente basato sulla massa limitata
fig = px.scatter(
filtered_data_non_null,
x="Orbit Semi-Major Axis",
y="Orbital Period Days",
color="Mass (Capped)", # Usare la massa limitata per il gradiente
color_continuous_scale="Viridis", # Scala colore continua
labels={
"Orbit Semi-Major Axis": "Orbit Semi-Major Axis [AU]",
"Orbital Period Days": "Orbital Period [Days]",
"Mass (Capped)": "Planet Mass"
},
title="Orbital Period vs Semi-Major Axis Colored by Planet Mass",
template="plotly_white",
log_x=True, # Scala logaritmica sull'asse X
log_y=True # Scala logaritmica sull'asse Y
)
# Formattare il grafico per slide PowerPoint
fig.update_layout(
width=1280,
height=720
)
# Mostrare il grafico
fig.show()
Relationship Between Stellar Surface Gravity and Mass with Stellar Radius Highlighted¶
In [13]:
# Filtrare i dati per escludere valori nulli
filtered_data_non_null = filtered_data.dropna(subset=["Stellar Radius", "Stellar Mass", "Stellar Surface Gravity"])
# Creare una colonna personalizzata per limitare il raggio a 5
filtered_data_non_null["Stellar Radius (Capped)"] = filtered_data_non_null["Stellar Radius"].apply(lambda x: min(x, 5))
# Creare il grafico scatter
fig = px.scatter(
filtered_data_non_null,
x="Stellar Mass",
y="Stellar Surface Gravity",
color="Stellar Radius (Capped)", # Utilizza il raggio limitato come scala colore
labels={
"Stellar Mass": "Stellar Mass [Solar Masses]",
"Stellar Surface Gravity": "Stellar Surface Gravity (log g)",
"Stellar Radius (Capped)": "Stellar Radius"
},
title="Stellar Surface Gravity vs Stellar Mass with Stellar Radius",
template="plotly_white",
color_continuous_scale="Jet" # Utilizza una scala di colori
)
# Configurare l'intervallo degli assi
fig.update_layout(
yaxis=dict(
range=[0, 6], # Limita l'intervallo tra 0 e 6
title="Stellar Surface Gravity (log g)"
),
xaxis=dict(
range=[0, 5], # Limita l'intervallo tra 0 e 5
title="Stellar Mass [Solar Masses]"
),
width=1280, # Larghezza per PowerPoint
height=720 # Altezza per PowerPoint
)
fig.update_coloraxes(
colorbar_title="Stellar Radius [solar R]",
cmin=filtered_data_non_null["Stellar Radius (Capped)"].min(), # Valore minimo
cmax=5, # Limita il valore massimo a 5
colorbar=dict(
tickvals=[filtered_data_non_null["Stellar Radius (Capped)"].min(), 1, 2, 3, 4, 5], # Tick personalizzati
ticktext=["0", "1", "2", "3", "4", "5+"]
)
)
# Visualizzare il grafico
fig.show()